import {StructArrayMember, StructArray, SerializedStructArray} from "../util/StructArray";
import {Program} from "../render/Program";

/**
 * @enum {string} AttributeType
 * @private
 * @readonly
 */
const AttributeType = [
  "BYTE",
  "UNSIGNED_BYTE",
  "SHORT",
  "UNSIGNED_SHORT",
  "INT",
  "UNSIGNED_INT",
  "FLOAT"
];
// enum ViewType {
//     Int8 = 0, Uint8, Int16, Uint16, Int32, Uint32, Float32
// }
/**
 * `VertexBuffer`将`StructArray`转成WebGL缓冲区。`StructArray`元素的每个成员都转换成
 * WebGL的`attribute`（比如a_pos）.
 *
 * @export
 * @class VertexBuffer
 */
export class VertexBuffer {
  length: number;
  attributes: Array<StructArrayMember>;
  itemSize: number;
  dynamicDraw?: boolean;
  gl: WebGLRenderingContext;
  buffer: WebGLBuffer | null;

  /**
   * @param dynamicDraw Whether this buffer will be repeatedly updated.
   */
  constructor(gl: WebGLRenderingContext, array: StructArray, dynamicDraw?: boolean) {
    this.length = array.length;
    this.attributes = array.members;
    this.itemSize = array.bytesPerElement;
    this.dynamicDraw = dynamicDraw;

    this.gl = gl;
    this.buffer = gl.createBuffer();
    this.gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
    this.gl.bufferData(gl.ARRAY_BUFFER, array.arrayBuffer, this.dynamicDraw ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);

    if (!this.dynamicDraw) {
      delete array.arrayBuffer;
    }
  }

  bind() {
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
  }

  updateData(array: SerializedStructArray) {
    this.bind();
    this.gl.bufferSubData(this.gl.ARRAY_BUFFER, 0, array.arrayBuffer);
  }

  /**
   * `Program`中的attribute需要和StructArray元素的某个成员对应，这样才能绑定缓冲区。
   * `gl.enableVertexAttribArray`将指定索引位置处的vertex attribute array打开，之后就可以
   * 从缓冲区读取数据到这些打开的vertex attribute array。
   *
   * @param {WebGLRenderingContext} gl
   * @param {Program} program
   * @memberof VertexBuffer
   */
  enableAttributes(gl: WebGLRenderingContext, program: Program) {
    for (let j = 0; j < this.attributes.length; j++) {
      const member = this.attributes[j];
      const attribIndex: number | void = program.attributes[member.name];
      if (attribIndex !== undefined) {
        gl.enableVertexAttribArray(attribIndex);
      }
    }
  }

  /**
   * 设置vertex attribute的指针信息
   *
   * @param {WebGLRenderingContext} gl
   * @param {Program} program
   * @param {number} [vertexOffset]
   * @memberof VertexBuffer
   */
  setVertexAttribPointers(gl: WebGLRenderingContext, program: Program, vertexOffset?: number) {
    for (let j = 0; j < this.attributes.length; j++) {
      const member = this.attributes[j];
      const attribIndex: number | void = program.attributes[member.name];

      if (attribIndex !== undefined) {
        gl.vertexAttribPointer(
          attribIndex,
          member.components,
          (<any>gl)[AttributeType[member.type]],
          false,
          this.itemSize,  // 一个数组元素有多个成员，它们在缓冲区数据里不是连续的，需要设置stride
          member.offset + (this.itemSize * (vertexOffset || 0))
        );
      }
    }
  }

  /**
   * Destroy the GL buffer bound to the given WebGL context
   */
  destroy() {
    if (this.buffer) {
      this.gl.deleteBuffer(this.buffer);
      delete this.buffer;
    }
  }
}